﻿using MCUPackage;
using MCUPackage.DesignEditor;
using MCUPackage.Utils;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Linq;
using System.Text;

namespace SxPackages.STC8.Models
{
	/// <summary>
	/// ADP配置选项
	/// </summary>
	[Serializable]
	class ADCOptions
	{
		protected STC8HUSOP20 mParent = null;

		private bool mEnable = false;
		/// <summary>
		/// 是否启用ADC
		/// </summary>
		[Category("ADC配置"),
		DisplayName("功能启用"),
		Description("是否启用ADC功能"),
		JsonProperty(Order = 100),
		DefaultValue(false)]
		public bool Enable
		{
			get => mEnable;
			set
			{
				if (value != mEnable)
				{
					string[] pins = GetPins(this.mUsePins);
					if (value)
					{
						// 启用时对引脚进行检查
						if (mParent.PinIsUsedTips(pins))
						{
							return;
						}
						SetADC(pins);
					}
					else
					{
						// 禁用时将引脚从列表中清除
						mParent.ResetPins(pins);
					}
					mEnable = value;
				}
			}
		}

		/// <summary>
		/// 获取配置后的时钟速度
		/// </summary>
		/// <returns></returns>
		private void PrintClock()
		{
			double tmp = mParent.mSystem.FreqValue / (2 * mSpeed);
			mParent.UpdateMsg($"ADC时钟频率为:{tmp:F3}KHz.");
		}

		private int mSpeed = 16;
		[Category("ADC配置"),
		DisplayName("ADC时钟"),
		Description("该参数为系统时钟的分频系数, 用于ADC时钟, 取值范围1~16"),
		DefaultValue(16)]
		public int Speed
		{
			get => mSpeed;
			set
			{
				if (value != mSpeed)
				{
					if (value < 1 || value > 16)
					{
						GuiUtils.MsgBox("请输入有效值!");
					}
					else
					{
						mSpeed = value;
						PrintClock();
					}
				}
			}
		}

		private int mSMPduty = 11;
		/// <summary>
		/// 采样时间控制
		/// </summary>
		[Category("ADC配置"),
		DisplayName("采样时间"),
		Description("ADC模拟信号采样时间控制, 时钟数, 设置值:11~32"),
		DefaultValue(11)]
		public int SMPduty
		{
			get => mSMPduty;
			set
			{
				if (mSMPduty != value)
				{
					if (value < 11 || value > 32)
					{
						GuiUtils.MsgBox("请输入有效值!");
					}
					else
					{
						mSMPduty = value;
						PrintSpeed();
					}
				}
			}
		}

		private int mCsSetup = 1;
		[Category("ADC配置"),
		DisplayName("通道选择时间"),
		Description("该参数为通道选择时间的时钟数, 取值范围1~2"),
		DefaultValue(1)]
		public int CsSetup
		{
			get => mCsSetup;
			set
			{
				if (value != mCsSetup)
				{
					if (value < 1 || value > 2)
					{
						GuiUtils.MsgBox("请输入有效值!");
					}
					else
					{
						mCsSetup = value;
						PrintSpeed();
					}
				}
			}
		}

		/// <summary>
		/// 获取转换速度KSPS
		/// </summary>
		/// <returns></returns>
		private void PrintSpeed()
		{
			double tmp = 2 * mSpeed * (mCsSetup + mCsHold + mSMPduty + 12);
			tmp = (mParent.mSystem.FreqValue) / tmp;
			mParent.UpdateMsg(string.Format("ADC转换速度:{0:F3}KSPS", tmp));
		}

		private int mCsHold = 2;
		[Category("ADC配置"),
		DisplayName("通道选择保持时间"),
		Description("该参数为通道选择保持时间的时钟数, 取值范围1~4"),
		DefaultValue(2)]
		public int CsHold
		{
			get => mCsHold;
			set
			{
				if (value != mCsHold)
				{
					if (value < 1 || value > 4)
					{
						GuiUtils.MsgBox("请输入有效值!");
					}
					else
					{
						mCsHold = value;
						PrintSpeed();
					}
				}
			}
		}

		[Category("ADC配置"),
		DisplayName("结果格式"),
		Description("ADC转换结果数据对齐设置, 左对齐则ADC_RES存高8位, 右对齐则ADC_RES存低8位"),
		DefaultValue(ADCAlign.左对齐)]
		public ADCAlign Align { get; set; } = ADCAlign.左对齐;

		[Category("ADC配置"),
		DisplayName("中断使能"),
		Description("是否开启ADC中断"),
		DefaultValue(false)]
		public bool EnableTI { get; set; } = false;

		[Category("ADC配置"),
		DisplayName("中断优先级"),
		Description("中断优先级等级设置"),
		DefaultValue(PriorityTypes.最低级)]
		public PriorityTypes Priority { get; set; } = PriorityTypes.最低级;

		private string mUsePins = string.Empty;
		[Category("ADC配置"),
		DisplayName("通道引脚选择"),
		Description("选择指定引脚作为ADC输入通道"),
		MultiCollection(new string[] { "P1.0 通道0", "P1.1 通道1", "P5.4 通道2", "P1.3 通道3", "P1.4 通道4",
										"P1.5 通道5", "P1.6 通道6", "P1.7 通道7" }),
		Editor(typeof(MultiCollectionEditor), typeof(UITypeEditor))]
		public string UsePins
		{
			get => mUsePins;
			set
			{
				if (value != mUsePins)
				{
					if (this.Enable)
					{
						string[] oldPins = GetPins(mUsePins);
						string[] newPins = GetPins(value);
						// 使能功能时才检查
						if (!CheckNewPins(newPins, oldPins))
						{
							return;
						}
						// 清理之前选中的引脚
						mParent.ResetPins(oldPins);
						// 添加当前选中引脚
						SetADC(newPins);
					}
					mUsePins = value;
				}
			}
		}

		[Category("ADC配置"),
		DisplayName("使能内部电压"),
		DefaultValue(false),
		Description("是否使能内部1.19V电压检测(ADC通道15)")]
		public bool EnableVol { get; set; } = false;

		/// <summary>
		/// 整理引脚名集合
		/// </summary>
		/// <param name="aPins"></param>
		/// <returns></returns>
		private string[] GetPins(string aPins)
		{
			string[] pins = TemplateHelper.GetPins(aPins);

			for (int i = 0; i < pins.Length; i++)
			{
				string str = pins[i];
				pins[i] = str.Substring(0, str.IndexOf(' '));
			}

			return pins;
		}

		/// <summary>
		/// 检查新引脚是否被占用
		/// </summary>
		/// <param name="aNew"></param>
		/// <param name="aOld"></param>
		/// <returns></returns>
		private bool CheckNewPins(string[] aNew, string[] aOld)
		{
			List<string> pins = new List<String>();
			if (aNew == null || aNew.Length < 1)
			{
				return true;
			}

			foreach (var item in aNew)
			{
				if (!aOld.Contains(item))
				{
					// 有新的引脚
					pins.Add(item);
				}
			}
			if (pins.Count < 1)
			{
				// 没有新增引脚
				return true;
			}

			return !mParent.PinIsUsedTips(pins.ToArray());
		}

		/// <summary>
		/// 获取引脚的属性
		/// </summary>
		/// <param name="aPin"></param>
		/// <returns></returns>
		private PinPurpose GetPurpose(string aPin)
		{
			var pins = MultiCollectionAttribute.GetCollection(typeof(ADCOptions), "UsePins");
			for (int i = 0; i < pins.Length; i++)
			{
				if (pins[i].Contains(aPin))
				{
					return PinPurpose.ADC0 + i;
				}
			}

			return PinPurpose.ADC0;
		}

		/// <summary>
		/// 设置指定引脚为ADC
		/// </summary>
		/// <param name="aName"></param>
		private void SetADC(string[] aPins)
		{
			foreach (var pin in aPins)
			{
				var option = mParent.SetPinPurpose(pin, GetPurpose(pin));
				if (option != null)
				{
					option.Mode = GPIOMode.HighInput;
					option.EnableIE = false;
				}
			}
		}

		/// <summary>
		/// 参数初始化模板
		/// </summary>
		private const string TEMPLATE =
@"	attr.ADC_SMPduty   = {0};		//ADC 模拟信号采样时间控制, 0~31（注意： SMPDUTY 一定不能设置小于 10）
	attr.ADC_CsSetup   = {1};		//ADC 通道选择时间控制 0(默认),1
	attr.ADC_CsHold    = {2};		//ADC 通道选择保持时间控制 0,1(默认),2,3
	attr.ADC_Speed     = ADC_SPEED_2X{3}T;		//设置 ADC 工作时钟频率	ADC_SPEED_2X1T~ADC_SPEED_2X16T
	attr.ADC_AdjResult = {4};	//ADC结果调整,	ADC_LEFT_JUSTIFIED,ADC_RIGHT_JUSTIFIED
	ADC_Inilize(&attr);		//初始化
	ADC_PowerControl(ENABLE);						//ADC电源开关, ENABLE或DISABLE
	NVIC_ADC_Init({5}, {6});		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3";

		/// <summary>
		/// 获取配置内容
		/// </summary>
		/// <returns></returns>
		public bool AppendConfig(List<string[]> aLst)
		{
			if (!this.Enable)
			{
				return false;
			}

			string name = "ADC_Config";
			StringBuilder tmpSb = new StringBuilder();
			tmpSb.Append(TemplateHelper.GetFunctionCommit("ADC模块初始化"));
			tmpSb.AppendLine($"PRIVATE void {name}(void)").AppendLine("{");
			tmpSb.AppendLine("\tADC_InitTypeDef attr;");

			// 通道数
			int chn = 0;
			UInt16 chnCfg = 0x00;
			string[] pins = GetPins(this.mUsePins);
			if (pins.Length > 0)
			{
				// 初始化引脚
				Dictionary<string, List<string>> lst = new Dictionary<string, List<string>>();
				foreach (var pin in pins)
				{
					var opt = mParent.GetPinOption(pin);
					if (opt != null)
					{
						opt.ToSTC8Dictionary(lst);
						if (opt.Purpose >= PinPurpose.ADC0 && opt.Purpose <= PinPurpose.ADC7)
						{
							chn++;
							chnCfg |= (UInt16)(1 << (opt.Purpose - PinPurpose.ADC0));
						}
					}
				}
				if (lst.Count > 0)
				{
					tmpSb.AppendLine();
					tmpSb.Append(GPIOOptions.GetGPIOConfigs(lst));
				}
			}

			// ADC配置
			if (this.EnableVol)
			{
				// 通道15的配置
				chn++;
				chnCfg |= 0x8000;
			}

			tmpSb.AppendLine();
			tmpSb.AppendFormat(TEMPLATE, this.SMPduty - 1, this.CsSetup - 1, this.CsHold - 1,
							this.Speed, this.Align.ToDescription(), this.EnableTI.ToEnable(), this.Priority.ToDescription()).AppendLine();

			tmpSb.AppendLine("}");
			aLst.Add(new string[] { tmpSb.ToString(), $"{name}();" });
			return true;
		}

		public ADCOptions(STC8HUSOP20 aParent)
		{
			this.mParent = aParent;
		}
	}
}
